package pl.wroc.uni.ii.pastuszka.algorithms.evolutionary.genetic;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import pl.wroc.uni.ii.pastuszka.algorithms.evolutionary.Individual;
import pl.wroc.uni.ii.pastuszka.algorithms.evolutionary.rankers.IndividualRanker;
import pl.wroc.uni.ii.pastuszka.algorithms.evolutionary.terminators.TerminationCondition;

abstract public class GeneticAlgorithmSchema {
  protected ArrayList<Individual> population;
  protected int populationSize;
  protected int chromosomeLength;

  protected IndividualRanker ranker;

  public void setPopulationSize(int populationSize) {
    this.populationSize = populationSize;
  }

  public void setChromosomeLength(int chromosomeLength) {
    this.chromosomeLength = chromosomeLength;
  }

  public GeneticAlgorithmSchema() {
  }

  public GeneticAlgorithmSchema(int chromosomeLength, int populationSize) {
    setChromosomeLength(chromosomeLength);
    setPopulationSize(populationSize);
  }

  protected void initializePopulation() {
    population = new ArrayList<Individual>(populationSize);
    for (int i = 0; i < populationSize; ++i) {
      population.add(new Individual(chromosomeLength));
    }
  }

  protected void evaluatePopulation(Collection<Individual> individuals) {
    for (Individual creature : individuals) {
      creature.rank = ranker.rank(creature);
    }
  }

  protected void evaluatePopulation() {
    evaluatePopulation(population);
  }

  protected void createRandomPopulation(float[] probabilityVector) {
    for (int i = 0; i < populationSize; ++i) {
      population.get(i).fillWithRandomData(probabilityVector);
    }
  }

  protected void createRandomPopulation() {
    for (int i = 0; i < populationSize; ++i) {
      population.get(i).fillWithRandomData();
    }
  }

  abstract protected void applyGeneticOperators();

  protected Individual findBestIndividual() {
    return Collections.max(population, Individual.RANK_COMPARATOR);
  }

  public Individual run(IndividualRanker ranker, TerminationCondition terminator) {
    this.ranker = ranker;

    initializePopulation();
    initializeAlgorithm();
    terminator.initialize(population);

    while (terminator.terminate() == false) {
      applyGeneticOperators();
      finishIteration();
      terminator.finishIteration(population);
    }

    return findBestIndividual();
  }

  protected void finishIteration() {
    evaluatePopulation();
  }

  protected void initializeAlgorithm() {
    createRandomPopulation();
    evaluatePopulation();
  }
}
